{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "210af3e2-8e9d-470d-b945-5d035bd67205",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "324c9335-f06e-4806-b25e-4788b9de635a",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f4ca264-1fbc-4afa-a974-c1a611de5941",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cee09df6-26d9-4516-a46f-d09781d7ba1f",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f88f65e8-c8b5-4400-9d7a-56140531b2fb",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bd8e3012-7c06-409d-a29b-ace08bc23658",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "434f767f-4b48-49ea-933e-ad3b96e9c12d",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d2daf82c-072d-4e20-8b1e-1ffdb693a9fe",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "134e7dbc-4cf5-4042-8814-118fbbbe105f",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "89e37c19-d2f8-4391-9317-e8c24640e0bf",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af59fc20-0dae-4278-954d-5cc3073fd52a",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9d6d3e09-52c2-4701-a842-750ceef3a37f",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "69b0c49d-b83e-4b58-bb9d-a2ba31247c88",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5b66de8-9e70-48e9-9fc8-f98e39af3ccf",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e833016-3c32-4b75-8a1a-3f56e657d97d",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "641f0d74-55e2-4e52-a061-e394997a521c",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "030f5536-7855-4389-b23f-9150be96a869",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8edea358-9a01-4ee3-b3e8-4afd7d10a5ee",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b8853774-7f11-4aed-9755-e7b6f2223508",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b6646611-b922-464c-a5f4-faf498d0b6e3",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "43be6cd8-909b-48cf-b4d5-c59492bd375e",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40cb125a-490f-4301-b8de-9268a7985b88",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c069b4b1-407c-45b1-bfd8-4a289ccdacb1",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f6069aa-912f-479f-86a5-af8acf2a34df",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fd829ae1-f31f-48af-a726-09c2214be3b8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2898c5b9-445c-40c6-997e-0cca58f445fe",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5da5dbe7-940c-4da9-ab46-8e828b1265f3",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1ecef94-d473-4d8b-8149-b6b434e1ed3b",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c7e766c8-8264-4ae8-a927-de7098195716",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99fadec3-8a7b-4720-8b25-12a27190291a",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8311d9cb-626d-49ef-96d1-f4a4bb9fd9e4",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from pandas import DataFrame\n",
    "\n",
    "\n",
    "class DataHelper:\n",
    "    @staticmethod\n",
    "    def get_dataset_min_max(dataset: DataFrame):\n",
    "        array = np.array(dataset)\n",
    "        return [np.amin(array, axis=0), np.amax(array, axis=0)]\n",
    "\n",
    "    @staticmethod\n",
    "    def find_category_columns(dataset: DataFrame):\n",
    "        categorical = []\n",
    "\n",
    "        for col in dataset.columns:\n",
    "            if len(dataset[col].unique()) < 10:\n",
    "                categorical.append(col)\n",
    "\n",
    "        return categorical\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f02066f2-0105-430f-8455-5e2543060f09",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import shutil\n",
    "\n",
    "\n",
    "class CreatedPlotsSaver:\n",
    "    @staticmethod\n",
    "    def save_plots(from_path: str, to_path: str) -> None:\n",
    "        files = os.listdir(from_path)\n",
    "        for file in files:\n",
    "            if \".png\" in file:\n",
    "                shutil.copy2(f\"{from_path}\\\\{file}\", f\"{to_path}\\\\{file}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f129ffa5-2ff0-47de-9800-b5483e727914",
   "metadata": {},
   "outputs": [],
   "source": [
    "from enum import Enum\n",
    "import pandas as pd\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from xgboost import XGBClassifier\n",
    "from sklearn.neural_network import MLPClassifier\n",
    "from typing import Union\n",
    "\n",
    "\n",
    "class DefaultModelType(Enum):\n",
    "    FOREST = (\n",
    "        RandomForestClassifier,\n",
    "        {\n",
    "            \"n_estimators\": 40,\n",
    "            \"random_state\": 42,\n",
    "            \"criterion\": \"gini\",\n",
    "            \"max_depth\": None,\n",
    "            \"min_samples_split\": 2,\n",
    "            \"min_samples_leaf\": 1,\n",
    "            \"min_weight_fraction_leaf\": 0.0,\n",
    "            \"max_features\": \"sqrt\",\n",
    "        },\n",
    "    )\n",
    "    XGBOOST = (\n",
    "        XGBClassifier,\n",
    "        {\n",
    "            \"n_estimators\": 100,\n",
    "            \"random_state\": 42,\n",
    "            \"max_depth\": None,\n",
    "        },\n",
    "    )\n",
    "    MLP = (\n",
    "        MLPClassifier,\n",
    "        {\n",
    "            \"hidden_layer_sizes\": (100,),\n",
    "            \"activation\": \"relu\",\n",
    "            \"solver\": \"lbfgs\",\n",
    "            \"alpha\": 1e-5,\n",
    "            \"batch_size\": \"auto\",\n",
    "        },\n",
    "    )\n",
    "\n",
    "\n",
    "class DefaultModelCreator:\n",
    "    def __init__(\n",
    "        self,\n",
    "        model_type: DefaultModelType,\n",
    "    ) -> None:\n",
    "        self._model_class = model_type.value[0]\n",
    "        self._model_params = model_type.value[1]\n",
    "\n",
    "        self._model_fitted = False\n",
    "\n",
    "    def fit_model(\n",
    "        self, X: pd.DataFrame, y: pd.DataFrame\n",
    "    ) -> Union[RandomForestClassifier, XGBClassifier, MLPClassifier]:\n",
    "        model = self._init_model()\n",
    "        fitted_model = self._fit_model(model, X, y)\n",
    "        self._model = fitted_model\n",
    "        return fitted_model\n",
    "\n",
    "    def get_model_accuracy(self):\n",
    "        if self._model_fitted:\n",
    "            return {\n",
    "                \"train\": self._model.score(self._X_train, self._y_train),\n",
    "                \"test\": self._model.score(self._X_test, self._y_test),\n",
    "            }\n",
    "\n",
    "    def _init_model(self):\n",
    "        model = self._model_class(**self._model_params)\n",
    "        return model\n",
    "\n",
    "    def _fit_model(self, model, X, y):\n",
    "        self._X_train, self._X_test, self._y_train, self._y_test = (\n",
    "            self._get_splited_dataset(X, y)\n",
    "        )\n",
    "        fitted_model = model.fit(self._X_train, self._y_train)\n",
    "        self._model_fitted = True\n",
    "        return fitted_model\n",
    "\n",
    "    @staticmethod\n",
    "    def _get_splited_dataset(X, y):\n",
    "        return train_test_split(X, y, test_size=0.15, random_state=42)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32b5c91e-42dd-4246-ac38-980d0b54bee0",
   "metadata": {},
   "outputs": [],
   "source": [
    "import seaborn as sns\n",
    "from pandas import DataFrame\n",
    "\n",
    "\n",
    "class DefaultDatasetCreator:\n",
    "    def __init__(self) -> None:\n",
    "        self.dataset = self._get_default_dataset()\n",
    "        self.X = self.dataset.drop(\"survived\", axis=1)\n",
    "        self.y = self.dataset[\"survived\"]\n",
    "\n",
    "    def get_dataset(self) -> DataFrame:\n",
    "        return self.dataset\n",
    "\n",
    "    def get_X(self):\n",
    "        return self.X\n",
    "\n",
    "    def get_y(self):\n",
    "        return self.y\n",
    "\n",
    "    def _get_default_dataset(self):\n",
    "        dataset = self._get_original_dataset()\n",
    "        dataset = self._drop_extra_fields(dataset)\n",
    "        dataset = self._fill_missing_values(dataset)\n",
    "        dataset = self._map_not_numeric_feature(dataset)\n",
    "\n",
    "        return dataset\n",
    "\n",
    "    @staticmethod\n",
    "    def _get_original_dataset():\n",
    "        dataset = sns.load_dataset(\"titanic\")\n",
    "\n",
    "        return dataset\n",
    "\n",
    "    @staticmethod\n",
    "    def _drop_extra_fields(dataset):\n",
    "        dataset = dataset.drop(\"embark_town\", axis=1)\n",
    "        dataset = dataset.drop(\"alive\", axis=1)\n",
    "        dataset = dataset.drop(\"who\", axis=1)\n",
    "        dataset = dataset.drop(\"pclass\", axis=1)\n",
    "        dataset = dataset.drop(\"deck\", axis=1)\n",
    "        dataset = dataset.drop(\"parch\", axis=1)\n",
    "        dataset = dataset.drop(\"adult_male\", axis=1)\n",
    "\n",
    "        return dataset\n",
    "\n",
    "    @staticmethod\n",
    "    def _fill_missing_values(dataset):\n",
    "        dataset[\"age\"] = dataset[\"age\"].fillna(dataset[\"age\"].mean())\n",
    "        freq_port = dataset.embarked.dropna().mode()[0]\n",
    "        dataset[\"embarked\"] = dataset[\"embarked\"].fillna(freq_port)\n",
    "\n",
    "        return dataset\n",
    "\n",
    "    @staticmethod\n",
    "    def _map_not_numeric_feature(dataset):\n",
    "        params = {\n",
    "            \"sex\": {\"female\": 1, \"male\": 0},\n",
    "            \"class\": {\"Third\": 3, \"Second\": 2, \"First\": 1},\n",
    "            \"alone\": {True: 1, False: 0},\n",
    "            # \"adult_male\": {True: 1, False: 0},\n",
    "            \"embarked\": {\"S\": 0, \"C\": 1, \"Q\": 2},\n",
    "        }\n",
    "        dataset[\"sex\"] = dataset[\"sex\"].map(params[\"sex\"]).astype(int)\n",
    "        dataset[\"class\"] = dataset[\"class\"].map(params[\"class\"]).astype(int)\n",
    "        dataset[\"alone\"] = dataset[\"alone\"].map(params[\"alone\"]).astype(int)\n",
    "        # dataset[\"adult_male\"] = (\n",
    "        #     dataset[\"adult_male\"].map(params[\"adult_male\"]).astype(int)\n",
    "        # )\n",
    "        dataset[\"embarked\"] = dataset[\"embarked\"].map(params[\"embarked\"]).astype(int)\n",
    "\n",
    "        return dataset\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "88ab3b80-79a7-4a4b-8b6b-7dea9a46ddc9",
   "metadata": {},
   "outputs": [],
   "source": [
    "from core.services import DefaultDatasetCreator, DefaultModelType, DefaultModelCreator\n",
    "\n",
    "\n",
    "class DebugStarter:\n",
    "    def __init__(self, model: str):\n",
    "        if model == \"forest\":\n",
    "            model_type = DefaultModelType.FOREST\n",
    "        elif model == \"neuro\":\n",
    "            model_type = DefaultModelType.MLP\n",
    "        elif model == \"boost\":\n",
    "            model_type = DefaultModelType.XGBOOST\n",
    "        else:\n",
    "            model_type = DefaultModelType.FOREST\n",
    "\n",
    "        self._dataset_creator = DefaultDatasetCreator()\n",
    "        self._model_creator = DefaultModelCreator(model_type)\n",
    "\n",
    "    def get_dataset_and_model(self):\n",
    "        dataset = self._get_dataset()\n",
    "        model = self._get_model()\n",
    "\n",
    "        return dataset, model\n",
    "\n",
    "    def _get_dataset(self):\n",
    "        dataset = self._dataset_creator.get_dataset()\n",
    "\n",
    "        return dataset\n",
    "\n",
    "    def _get_model(self):\n",
    "        X = self._dataset_creator.get_X()\n",
    "        y = self._dataset_creator.get_y()\n",
    "        model = self._model_creator.fit_model(X, y)\n",
    "\n",
    "        return model\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e934546e-b85e-4612-b4b2-eb510b39e46e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Optional\n",
    "\n",
    "from pydantic import BaseModel\n",
    "\n",
    "\n",
    "class PlotSettings(BaseModel):\n",
    "    min_value: float\n",
    "    max_value: float\n",
    "    column: str\n",
    "    category_column: Optional[str] = None\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "173470bb-9d31-4d36-9496-d511b1facff2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from PyQt5 import QtGui\n",
    "\n",
    "\n",
    "STYLESHEET = \"\"\"\n",
    "    border-bottom-width: 1px;\n",
    "    border-bottom-style: solid;\n",
    "    border-top-width: 1px;\n",
    "    border-top-style: solid;\n",
    "    border-left-width: 1px;\n",
    "    border-left-style: solid;\n",
    "    border-right-width: 1px;\n",
    "    border-right-style: solid;\n",
    "    border-radius: 0px;\n",
    "\"\"\"\n",
    "\n",
    "\n",
    "COLORS = [\"brown\", \"teal\", \"blue\", \"coral\", \"limegreen\", \"pink\", \"olive\", \"navy\", \"red\"]\n",
    "FONT_SIZE = 18\n",
    "FIG_COUNT = 1\n",
    "\n",
    "PLOT_WIDTH = 12\n",
    "PLOT_HEIGHT = 6\n",
    "\n",
    "FIG_WIDTH = PLOT_WIDTH * 100\n",
    "FIG_HEIGHT = PLOT_HEIGHT * 100\n",
    "\n",
    "FIG_SIZE = (FIG_WIDTH, FIG_HEIGHT)\n",
    "DPI = 2\n",
    "\n",
    "\n",
    "SAVE_COMPLETE_MESSAGE = \"Сохранение завершено\"\n",
    "\n",
    "DATASET_LOAD_ERROR_MESSAGE = \"Загруженный объект не является выборкой\"\n",
    "MODEL_LOAD_ERROR = \"Загруженный объект не является выборкой\"\n",
    "\n",
    "DATA_IS_AWAITED_MESSAGE = \"Ожидается загрузка датасета и модели\"\n",
    "ONLY_MODEL_LOADED_MESSAGE = \"Загрузка модели произведена. Ожидается загрузка выборки\"\n",
    "ONLY_DATASET_LOADED_MESSAGE = \"Загрузка датасета произведена. Ожидается загрузка модели\"\n",
    "DATASET_AND_MODEL_LOADED_MESSAGE = \"Датасет и модель загружены\"\n",
    "\n",
    "NO_PLOTS_MESSAGE = \"Для сохранения постройте графики\"\n",
    "\n",
    "DEBUG_FILE_PATH = \"../data/obj_v2\"\n",
    "\n",
    "r_w = 255\n",
    "g_w = 255\n",
    "b_w = 255\n",
    "\n",
    "r_b = 235\n",
    "g_b = 204\n",
    "b_b = 153\n",
    "\n",
    "QT_COLOR_WHITE = QtGui.QColor(r_w, g_w, b_w)\n",
    "QT_COLOR_BEIGE = QtGui.QColor(r_b, g_b, b_b)\n",
    "\n",
    "CHAT_BASE_STYLESHEET = \"border: 1px solid black; background-color: rgb({}, {}, {}); \"\n",
    "CHAT_USER_STYLESHEET = CHAT_BASE_STYLESHEET.format(r_w, g_w, b_w)\n",
    "CHAT_LLM_STYLESHEET = CHAT_BASE_STYLESHEET.format(r_b, g_b, b_b)\n",
    "\n",
    "\n",
    "TOP5_CENTERED_IMPORTANCE_TITLE = \"Центрированный график изменения важности переменных\"\n",
    "\n",
    "ICE_IMPORTANCE_Y_LABEL = \"Важность переменной {}\"\n",
    "ICE_IMPORTANCE_TITLE = \"с-ICE график изменения важности переменной {}\"\n",
    "BASE_IMPORTANCE_TITLE = \"График важности переменной {} для исходных данных\"\n",
    "\n",
    "ICE_PREDICTIONS_Y_LABEL = \"Вероятность удачного исхода при изменении переменной {}\"\n",
    "ICE_PREDICTIONS_TITLE = (\n",
    "    \"с-ICE график вероятности удачного исхода при изменении переменной {}\"\n",
    ")\n",
    "\n",
    "PLOT_SETTINGS_ERROR_MESSAGE = (\n",
    "    \"Именна переменной варьирования и категориальной переменной не могут совпадать\"\n",
    ")\n",
    "\n",
    "\n",
    "TOKEN = \"y0_AgAAAAAdy8jaAATuwQAAAAEB9i4DAADeGpmlxK9DS5qcedkm1T8SG3Xh2A\"\n",
    "TOKEN_URL = \"https://iam.api.cloud.yandex.net/iam/v1/tokens\"\n",
    "LLM_FOLDER = \"b1gqlhmqdst9ejbptsnp\"\n",
    "LLM_URL = \"https://llm.api.cloud.yandex.net/foundationModels/v1/completion\"\n",
    "\n",
    "LLM_SYSTEM_PROMPT = \"\"\"\n",
    "Ты опытный аналитик, который профессионально умеет искать внутренние закономерности в данных. \n",
    "Тебе передали набор правил, которые описывают зависимость значений нескольких переменных от параметра PROBA. \n",
    "Чем выше значение параметра PROBA, тем выше вероятность отнесения конкретной записи к категории GOOD.\n",
    "Необходимо проанализировать правила и сделать следующие действия:\n",
    "1. Приведи пример двух записей с высокой вероятностью попадания в категорию GOOD\n",
    "2. Приведи пример двух записей со средней вероятностью попадания в категорию GOOD\n",
    "3. Приведи пример двух записей с низкой вероятностью попадания в категорию GOOD\n",
    "4. Приведи пример записей с самой низкой вероятностью попадания в категорию GOOD и с самой высокой\n",
    "Обязательно укажи вероятность в скобках у примеров\n",
    "Пиши краткое и по существу, основываясь на переданных данных, \n",
    "не пиши о предварительных данных и что нужно больше информации. \n",
    "Сравнение проводи по строгим математическим правилам\n",
    "\"\"\"\n",
    "\n",
    "LLM_SYSTEM_PROMPT_FOR_USER = \"\"\"\n",
    "Проанализируй правила правила и приведи примеры:\n",
    "1. Приведи пример двух записей с высокой вероятностью попадания в категорию GOOD\n",
    "2. Приведи пример двух записей со средней вероятностью попадания в категорию GOOD\n",
    "3. Приведи пример двух записей с низкой вероятностью попадания в категорию GOOD\n",
    "4. Приведи пример записей с самой низкой вероятностью попадания в категорию GOOD и с самой высокой\n",
    "\"\"\"\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "faffbb16-aef5-4167-8459-a9b0f8337ea9",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tempfile\n",
    "\n",
    "from app.constants import (\n",
    "    STYLESHEET,\n",
    "    DATASET_AND_MODEL_LOADED_MESSAGE,\n",
    "    DATA_IS_AWAITED_MESSAGE,\n",
    "    ONLY_DATASET_LOADED_MESSAGE,\n",
    "    ONLY_MODEL_LOADED_MESSAGE,\n",
    "    SAVE_COMPLETE_MESSAGE,\n",
    "    NO_PLOTS_MESSAGE,\n",
    "    FIG_WIDTH,\n",
    "    FIG_HEIGHT,\n",
    "    LLM_SYSTEM_PROMPT_FOR_USER,\n",
    ")\n",
    "from core.schemes import PlotSettings\n",
    "from core.debug_starter import DebugStarter\n",
    "from app.services.created_plots_saver import CreatedPlotsSaver\n",
    "from core.services.data_helper import DataHelper\n",
    "from pandas import DataFrame\n",
    "from xgboost import XGBClassifier\n",
    "from core.services.pickle_service import PickleService\n",
    "from PyQt5 import QtCore, QtWidgets, uic\n",
    "from core.schemes.pickled_data import DatasetModelMonoObject\n",
    "from app.services.qt_helper import QtHelper\n",
    "from app.components.plot_settings_app import PlotDataDialog\n",
    "from app.components.ai_settings_app import AISettingDialog\n",
    "from app.components.plot_container import PlotContainer\n",
    "from app.services.dataset_renderer import DatasetRendered\n",
    "from app.services.plot_creator import PlotCreator\n",
    "from core.services.llm_controller import LLMController\n",
    "from core.services.simple_tree_model_fitter import SimpleTreeModelFitter\n",
    "from core.services.model_rules_aggregator import ModelRulesAggregator\n",
    "from app.components.text_chat_scroll_widget import TextChatScrollArea\n",
    "\n",
    "\n",
    "class MainApp(QtWidgets.QMainWindow):\n",
    "    def __init__(self, parent=None):\n",
    "        super(MainApp, self).__init__()\n",
    "\n",
    "        self.__qt_helper = QtHelper()\n",
    "        self.__pickle_service = PickleService()\n",
    "        self.__data_helper = DataHelper()\n",
    "\n",
    "        uic.loadUi(\"../ui/main2.ui\", self)\n",
    "\n",
    "        self.open_file_action.setStatusTip(\"Открыть файл проекта\")\n",
    "        self.open_file_action.triggered.connect(self.open_saved_file)\n",
    "\n",
    "        self.save_file_action.setStatusTip(\"Сохранить файл модели и выборки\")\n",
    "        self.save_file_action.triggered.connect(self.save_file)\n",
    "\n",
    "        self.load_model_action.setStatusTip(\"Открыть файл модели\")\n",
    "        self.load_model_action.triggered.connect(self.open_model)\n",
    "\n",
    "        self.load_dataset_action.setStatusTip(\"Открыть файл выборки\")\n",
    "        self.load_dataset_action.triggered.connect(self.open_dataset)\n",
    "\n",
    "        self.save_plot_action.triggered.connect(self.save_plots)\n",
    "\n",
    "        self.push_button_plot.clicked.connect(self.make_plots)\n",
    "\n",
    "        self.user_input_button.clicked.connect(self.send_chat_request)\n",
    "        self.llm_init_button.clicked.connect(self.init_llm_button)\n",
    "\n",
    "        self._widget = QtWidgets.QWidget()\n",
    "        self._layout = self.horizontalLayout_8\n",
    "        self._scroll = TextChatScrollArea()\n",
    "        self._layout.addWidget(self._scroll)\n",
    "\n",
    "        self._widget.setLayout(self._layout)\n",
    "\n",
    "        self.plots_combo_box.currentIndexChanged.connect(self.switch_plots)\n",
    "\n",
    "        self.statusBar().showMessage(\"Ожидается загрузка модели и датасета\")\n",
    "\n",
    "        self.is_model_loaded = False\n",
    "        self.is_dataset_loaded = False\n",
    "\n",
    "        self.__model = None\n",
    "        self.__dataset = None\n",
    "\n",
    "        self.__paths: dict[str, list[str]] = {}\n",
    "\n",
    "        self.temp_dir = tempfile.TemporaryDirectory()\n",
    "\n",
    "        self.setWindowState(QtCore.Qt.WindowMaximized)\n",
    "        self.debug_start()\n",
    "        self.show()\n",
    "\n",
    "    @property\n",
    "    def dataset(self) -> DataFrame:\n",
    "        return self.__dataset\n",
    "\n",
    "    @property\n",
    "    def clean_dataset(self) -> DataFrame:\n",
    "        return self.__dataset.drop('survived', axis=1)\n",
    "\n",
    "    @dataset.setter\n",
    "    def dataset(self, dataset) -> None:\n",
    "        self.is_dataset_loaded = True\n",
    "        self.__dataset = dataset\n",
    "\n",
    "        self.create_columns()\n",
    "        self.create_rows()\n",
    "        self.show_data_status()\n",
    "\n",
    "    @property\n",
    "    def model(self) -> XGBClassifier:\n",
    "        return self.__model\n",
    "\n",
    "    @model.setter\n",
    "    def model(self, model) -> None:\n",
    "        self.is_model_loaded = True\n",
    "        self.__model = model\n",
    "        self.show_data_status()\n",
    "\n",
    "    def closeEvent(self, event):\n",
    "        self.temp_dir.cleanup()\n",
    "\n",
    "    def debug_start(self):\n",
    "        debug_starter = DebugStarter(\"boost\")\n",
    "        dataset, model = debug_starter.get_dataset_and_model()\n",
    "        self.dataset = dataset\n",
    "        self.model = model\n",
    "\n",
    "    def show_data_status(self) -> None:\n",
    "        if self.is_dataset_loaded and self.is_model_loaded:\n",
    "            self.statusBar().showMessage(DATASET_AND_MODEL_LOADED_MESSAGE)\n",
    "        elif self.is_dataset_loaded:\n",
    "            self.statusBar().showMessage(ONLY_DATASET_LOADED_MESSAGE)\n",
    "        elif self.is_model_loaded:\n",
    "            self.statusBar().showMessage(ONLY_MODEL_LOADED_MESSAGE)\n",
    "        else:\n",
    "            self.statusBar().showMessage(DATA_IS_AWAITED_MESSAGE)\n",
    "\n",
    "    def open_saved_file(self, path: str = \"\"):\n",
    "        if not path:\n",
    "            file_name = self.__qt_helper.get_path_to_open_file(self)\n",
    "        else:\n",
    "            file_name = path\n",
    "\n",
    "        if not file_name:\n",
    "            return\n",
    "\n",
    "        mono_object = self.__pickle_service.get_dataset_and_model(file_name)\n",
    "\n",
    "        self.model = mono_object.model\n",
    "        self.dataset = mono_object.dataset\n",
    "\n",
    "    def save_file(self):\n",
    "        if not all([self.is_dataset_loaded, self.is_model_loaded]):\n",
    "            self.show_data_status()\n",
    "            return\n",
    "\n",
    "        file_name = self.__qt_helper.get_path_to_save_file(self)\n",
    "\n",
    "        if not file_name:\n",
    "            return\n",
    "\n",
    "        mono = DatasetModelMonoObject(dataset=self.__dataset, model=self.model)\n",
    "        self.__pickle_service.save_dataset_and_model(mono, file_name)\n",
    "\n",
    "        self.statusBar().showMessage(SAVE_COMPLETE_MESSAGE)\n",
    "\n",
    "    def open_dataset(self):\n",
    "        path = self.__qt_helper.get_path_to_open_file(self)\n",
    "\n",
    "        if not path:\n",
    "            return None\n",
    "\n",
    "        self.dataset = self.__pickle_service.get_dataset(path)\n",
    "\n",
    "    def create_columns(self):\n",
    "        layout = DatasetRendered(self.dataset).get_rendered_info_plots_layout()\n",
    "        self.tabCollumns.setLayout(layout)\n",
    "\n",
    "    def create_rows(self):\n",
    "        layout = DatasetRendered(self.dataset).get_rendered_data_layout()\n",
    "        self.tabData.setLayout(layout)\n",
    "\n",
    "    def open_model(self):\n",
    "        file_name = self.__qt_helper.get_path_to_open_file(self)\n",
    "\n",
    "        if not file_name:\n",
    "            return\n",
    "\n",
    "        self.model = self.__pickle_service.get_model(file_name)\n",
    "\n",
    "    def save_plots(self):\n",
    "        if self.plots_combo_box.count() == 0:\n",
    "            self.statusBar().showMessage(NO_PLOTS_MESSAGE)\n",
    "            return\n",
    "\n",
    "        path = self.__qt_helper.get_existing_dir_path(self)\n",
    "\n",
    "        if not path:\n",
    "            return\n",
    "\n",
    "        CreatedPlotsSaver.save_plots(self.temp_dir.name, path)\n",
    "\n",
    "    def switch_plots(self):\n",
    "        if not self.is_plots_in_progress:\n",
    "            text = self.plots_combo_box.currentText()\n",
    "            if text:\n",
    "                self.show_plots(text)\n",
    "\n",
    "    def make_plots(self):\n",
    "        if not self.is_dataset_loaded and self.is_model_loaded:\n",
    "            self.show_data_status()\n",
    "\n",
    "        dialog = QtWidgets.QDialog()\n",
    "        self.ui = PlotDataDialog()\n",
    "        min_max = self.__data_helper.get_dataset_min_max(self.clean_dataset)\n",
    "        category_columns = self.__data_helper.find_category_columns(self.clean_dataset)\n",
    "        self.ui.setupUi(\n",
    "            dialog,\n",
    "            list(self.clean_dataset.columns),\n",
    "            category_columns,\n",
    "            min_max,\n",
    "            self.retrieve_data_from_child,\n",
    "        )\n",
    "        dialog.show()\n",
    "\n",
    "    def retrieve_data_from_child(self, settings: PlotSettings):\n",
    "        self.is_plots_in_progress = True\n",
    "\n",
    "        plot_creator = PlotCreator(\n",
    "            self.model,\n",
    "            self.dataset,\n",
    "            settings.column,\n",
    "            settings.min_value,\n",
    "            settings.max_value,\n",
    "            self.temp_dir,\n",
    "        )\n",
    "\n",
    "        if settings.category_column:\n",
    "            full_paths = plot_creator.plot_category_plots(settings.category_column)\n",
    "            self.__save_category_paths(full_paths, settings)\n",
    "        else:\n",
    "            paths = plot_creator.plot_regular_plots()\n",
    "            self.__save_regular_paths(paths, settings)\n",
    "\n",
    "        self.is_plots_in_progress = False\n",
    "\n",
    "        self.__update_plot_names_in_combo_box()\n",
    "        self.switch_plots()\n",
    "\n",
    "    def __save_category_paths(\n",
    "        self, full_paths: list[[list[str]]], settings: PlotSettings\n",
    "    ) -> None:\n",
    "        name = self.__get_name_for_settings(settings)\n",
    "        for index, path in enumerate(full_paths, start=1):\n",
    "            self.__paths[f\"{name} {index}\"] = path\n",
    "\n",
    "    def __save_regular_paths(self, paths: list[str], settings: PlotSettings) -> None:\n",
    "        name = self.__get_name_for_settings(settings)\n",
    "        self.__paths[name] = paths\n",
    "\n",
    "    def __get_name_for_settings(self, settings: PlotSettings) -> str:\n",
    "        name = f\"Колонка {settings.column} от {settings.min_value} и до {settings.max_value}\"\n",
    "\n",
    "        if settings.category_column:\n",
    "            name += f\" по категории {settings.category_column}\"\n",
    "\n",
    "        return name\n",
    "\n",
    "    def __update_plot_names_in_combo_box(self):\n",
    "        plot_names = list(self.__paths.keys())\n",
    "\n",
    "        self.plots_combo_box.clear()\n",
    "        self.plots_combo_box.addItems(plot_names)\n",
    "        self.plots_combo_box.setEnabled(True)\n",
    "        self.plots_combo_box.setCurrentIndex(len(plot_names) - 1)\n",
    "\n",
    "    def show_plots(self, plots_name: str):\n",
    "        paths = self.__paths[plots_name]\n",
    "        indexes = [(0, 0), (0, 1), (1, 0), (1, 1)]\n",
    "\n",
    "        for path, position in zip(paths, indexes):\n",
    "            pixmap = PlotContainer(path)\n",
    "            pixmap.setMaximumSize(FIG_WIDTH, FIG_HEIGHT)\n",
    "            pixmap.setStyleSheet(STYLESHEET)\n",
    "            pixmap.setSizePolicy(\n",
    "                QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum\n",
    "            )\n",
    "            widget_to_replace = self.gridLayout.itemAtPosition(*position)\n",
    "            widget_to_replace.widget().setParent(None)\n",
    "            self.gridLayout.addWidget(pixmap, *position)\n",
    "\n",
    "    def send_chat_request(self):\n",
    "        text = self.user_input_widget.document().toPlainText()\n",
    "        if text:\n",
    "            self._scroll.add_text(text)\n",
    "\n",
    "            response = self.llm_controller.get_answer(text)\n",
    "            self._scroll.add_text(response)\n",
    "\n",
    "            scroll_bar = self._scroll.verticalScrollBar()\n",
    "            scroll_bar.setValue(scroll_bar.maximum() + 500)\n",
    "\n",
    "    def init_llm_button(self):\n",
    "        if not self.is_dataset_loaded and self.is_model_loaded:\n",
    "            self.show_data_status()\n",
    "\n",
    "        dialog = QtWidgets.QDialog()\n",
    "        self.ui = AISettingDialog()\n",
    "        self.ui.setupUi(dialog, self._init_llm_button_callback)\n",
    "        dialog.show()\n",
    "\n",
    "    def _init_llm_button_callback(self, temperature, depth):\n",
    "        fitter = SimpleTreeModelFitter(self.model, self.dataset, depth)\n",
    "        tree = fitter.get_simple_tree()\n",
    "        columns = fitter.get_X().columns\n",
    "\n",
    "        aggregator = ModelRulesAggregator(tree, columns)\n",
    "        rules = aggregator.get_formatted_rules()\n",
    "\n",
    "        self.llm_controller = LLMController(temperature)\n",
    "        self.proba_text.setText(rules)\n",
    "        self._scroll.add_text(LLM_SYSTEM_PROMPT_FOR_USER)\n",
    "\n",
    "        response = self.llm_controller.get_answer(rules)\n",
    "        self._scroll.add_text(response)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "311e1829-e35c-420b-a599-2c19a53905f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "from PyQt5 import QtWidgets\n",
    "\n",
    "from app.components.main_app import MainApp\n",
    "\n",
    "\n",
    "def dalex_test():\n",
    "    import dalex as dx\n",
    "    from core.debug_starter import DebugStarter\n",
    "    starter = DebugStarter('forest')\n",
    "    df, model = starter.get_dataset_and_model()\n",
    "    X = df.drop('survived', axis=1)\n",
    "    y = df['survived']\n",
    "\n",
    "    titanic_rf_exp = dx.Explainer(model, X, y, label=\"Titanic RF Pipeline\")\n",
    "\n",
    "    test = X.iloc[0]\n",
    "    bd_henry = titanic_rf_exp.predict_parts(test, type='break_down')\n",
    "    print('ts')\n",
    "\n",
    "\n",
    "def get_GP_plots():\n",
    "    from libs.XAI.src.xai import GP\n",
    "    from core.debug_starter import DebugStarter\n",
    "    import pathlib\n",
    "    import os\n",
    "\n",
    "    if sys.platform == 'win32':\n",
    "        path = pathlib.Path(r'C:\\Program Files\\Graphviz\\bin')\n",
    "        if path.is_dir() and str(path) not in os.environ['PATH']:\n",
    "            os.environ['PATH'] += f';{path}'\n",
    "\n",
    "    starter = DebugStarter('forest')\n",
    "    df, model = starter.get_dataset_and_model()\n",
    "    X = df.drop('survived', axis=1)\n",
    "    # Train and predict with blackbox\n",
    "    predictions = [[x] for x in model.predict(X.values)]\n",
    "\n",
    "    # Use GP to make an approximation of the blackbox predictions\n",
    "    explainer = GP(max_trees=100, num_generations=1)\n",
    "    explainer.fit(X.values, predictions)\n",
    "\n",
    "    # Save our approximations\n",
    "    explainer.plot(\"./model.png\")\n",
    "    explainer.plot_pareto(\"./frontier.png\")\n",
    "\n",
    "\n",
    "def main():\n",
    "    app = QtWidgets.QApplication(sys.argv)\n",
    "    main_app = MainApp()\n",
    "    app.exec_()\n",
    "    # dalex_test()\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    main()\n",
    "\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
